/* The game. */!!

inherit(Window, #FunWindow, #(enemies    /* Bouncing balls */
enemyMove  /* event timer */
field      /* playing limits */
jewels     /* the points */
player     /* Player's position */
playerLife /* remaining lives */
score      /* pts. earned */
scoreBoard /* score display area */
time       /* timer */), 2, nil)!!

now(FunWindowClass)!!

/* Return the fun window style. */
Def style(self)
{ ^WS_OVERLAPPEDWINDOW
}!!

/* This space for rent. */
Def sizeRect(self)
{  ^rect(1,171,191,312)
}
!!

/* Return the name of this class's MS-Windows 
  window class either for registration or new 
  window creation. */
Def wndIcon(self)
{ ^"funicon"
}!!

/* Return the name of this class's MS-Windows window class
  either for registration or new window creation. */
Def wndClass(self)
{ ^"FunWindow"  }!!

/* Create a new window class Struct.
   Change the cursor to a cross.  */
Def newWClass(self, lpCl, lpIcon | wc)
{ wc := newWClass(self:WindowClass, lpCl, lpIcon);
  putWord(wc, Call LoadCursor(HInstance, asciiz("funcur")), 14);
  ^wc;
} !!

now(FunWindow)!!

/* We're really killing then creating */
/*  and returning a new jewel.        */
/**************************************/
Def  moveJewel(self, oldJewel | hDC, newJewel)
{  newJewel := new(Jewel);
   hDC := getContext(self);
   erase(oldJewel, hDC);
   draw(newJewel, hDC, field);
   releaseContext(self, hDC);
   ^newJewel;
}!!

/* Display about box. */
/**********************/
Def  WM_SYSCOMMAND(self, wP, lP)
{ if wP == IDSABOUT
  then runModal(new(Dialog), ABOUT_BOX, self);
  else ^asInt(execWindowProc(self, #WM_SYSCOMMAND, wP, lP));
  endif;
}!!

/* Put the player in a safe position. */
/**************************************/
Def  gotFocus(self, hWndPrev)
{ player := point(right(field), top(field));
} !!

/* Handle the expiring timer event. */
Def  WM_KILLFOCUS(self, wp, lp)
{ player := nil;
}!!

/* Return true if  inside client area. */
/***************************************/
Def  inField(self, pos)
{ if pos <> nil
  then if  pos.y <= bottom(field)
       and pos.x >= left(field)
       and pos.x <= right(field)
       and pos.y >= top(field)
       then ^true;
       endif;
  else ^false;
  endif;
}!!

/* Display the score */
Def  showScore(self | hDC)
{ hDC := getContext(self);
  Call TextOut(hDC, right(field)-35, 5,
               asString(score), size(asString(score)));
  releaseContext(self, hDC);
}!!

/* Move the guard one speed unit. */
/**********************************/
Def  moveGuards(self | hDC)
{ hDC := getContext(self);
   do(enemies,
   {using(guard)
     setGuardDir(self, guard);  /* Check direction */
     move(guard, hDC);          /* Move there */
     if inField(self, player)   /* See if we hit player */
     cand x(player)+7 >= x(guard) - size(guard)
     cand x(player)-7 <= x(guard)
     cand y(player)+7 >= y(guard) - size(guard)
     cand y(player)-7 <= y(guard)
     then playerLife := playerLife - 1;
          if playerLife > 0
          then beep();
          endif;
     endif;
   });
   releaseContext(self, hDC);
}!!

/* Bounce off the wall of the window and the scoreBoard. */
/*   Take into account the guard's speed and height so   */
/*   the guard is not half off the screen.               */
/*********************************************************/
Def  setGuardDir(self, guard | guardSize, guardSpeed)
{ guardSize := size(guard);
  guardSpeed := speed(guard);
  select
  /* bounce off bottom of window */
    case y(guard) + guardSpeed >= bottom(field)
    is setDir(guard, "N");
    endCase
  /* bounce off top of window, bottom of scoreboard */
    case y(guard) - guardSpeed <= bottom(scoreBoard) + guardSize
    is if x(guard) + guardSpeed >= left(scoreBoard)
       cor y(guard) - guardSpeed <= top(field) + guardSize
          setDir(guard, "S");
       endif;
    endCase
  endSelect;
/* Allow two direction changes per turn */
  select
  /* bounce off left of window */
    case x(guard) - guardSpeed <= left(field) + guardSize
    is setDir(guard, "E");
    endCase
  /* bounce of right of window, left of scoreboard */
    case x(guard) + guardSpeed >= left(scoreBoard)
    is if y(guard) - guardSpeed <= bottom(scoreBoard)
       cor x(guard) + guardSpeed >= right(field)
          setDir(guard, "W");
       endif;
    endCase
  endSelect;
}!!

/* Handle the expiring timer event. */
Def  WM_TIMER(self | redo)
{ if playerLife <> 0
  then moveGuards(self);
   time := time + 1;
   do(size(jewels),
     {using(idx)
     if age(jewels[idx]) <= 0
     then jewels[idx] := moveJewel(self, jewels[idx]);
     else rust(jewels[idx]);
     endif;
     });
  else playerLife := -1;
   redo := new(ErrorBox, ThePort,
           "Would you like to play again?",
           "Final Score: "+asString(score), 4);
   select
    case redo == IDYES
    is playerLife := 5;
       score := 0;
       repaint(self);
    endCase
    case redo == IDNO
    is Call KillTimer(hWnd, enemyMove);
       close(self);
    endCase
   endSelect;
  endif;
}!!

/* Respond to MS-Window's mouse move message. 
  Convert the mouse location data pointed to by 
  lp to a Point object. */
Def WM_MOUSEMOVE(self, wp, lp)
{ if inField(self, player)
  then player := asPoint(lp);
   do(size(jewels),
   {using(idx | jewelSize)
     jewelSize := size(jewels[idx]);
     if x(player)+7 >= x(jewels[idx])-jewelSize
     and x(player)-7 <= x(jewels[idx])+jewelSize
     and y(player)+7 >= y(jewels[idx])-jewelSize
     and y(player)-7 <= y(jewels[idx])+jewelSize
     then score := score + 1;
          showScore(self);
          jewels[idx] := moveJewel(self, jewels[idx]);
    endif;
  });
  endif;
}!!

/* Set field boundries on resize. */
Def  reSize(self, wP, lP)
{ field := clientRect(self);
  repaint(self);
}!!

/* Repaint items in window upon resizing. */
/******************************************/
Def  paint(self, hDC)
{ init(scoreBoard,right(field)-90,0,right(field),20);
  draw(scoreBoard, hDC);
  showScore(self);
  Call TextOut(hDC, right(field)-85, 5, "Score:", 6); 
  do(enemies,
    {using(enemy) draw(enemy, hDC)
    });
  do(jewels,
    {using(jewel) draw(jewel, hDC, field);
    });
}!!

/* Set up everything to play the game. */
/***************************************/
Def  init(self)
{ time := 0;                 /* Set initial time */
  score := 0;                /* Set initial score */
  playerLife := 5;           /* Lives remaining */
  addAbout(self);            /* Set up About box */
  field := clientRect(self); /* Set up playing field */
  scoreBoard := new(Rect);   /* Set up scoreBoard */
  enemies := new(Array,3);   /* Set up the guards */
  do(size(enemies),
    {using(enemy)
     enemies[enemy] := new(Guard);
    });
  jewels := new(Array,3);    /* Set up the jewels */
  do(size(jewels),
    {using(idx)
     jewels[idx] := new(Jewel); 
    });
                             /* Set up the timer */
  enemyMove := Call SetTimer(hWnd, 0, 1, 0);
}!!


